﻿/******************************************************************************
* SunnyUI.FrameDecoder 开源TCP、串口数据解码库。
* CopyRight (C) 2022-2023 ShenYongHua(沈永华).
* QQ群：56829229 QQ：17612584 EMail：SunnyUI@qq.com
*
* Blog:   https://www.cnblogs.com/yhuse
* Gitee:  https://gitee.com/yhuse/SunnyUI.FrameDecoder
*
* SunnyUI.FrameDecoder.dll can be used for free under the MIT license.
* If you use this code, please keep this note.
* 如果您使用此代码，请保留此说明。
******************************************************************************
* 文件名称: BaseByteFrameDecoder.cs
* 文件说明: 字节帧解码器基类
* 当前版本: V1.0
* 创建日期: 2022-11-01
*
* 2022-11-01: V1.0.0 增加文件说明
******************************************************************************/

using System;

namespace Sunny.FrameDecoder
{
    /// <summary>
    /// 字节帧解码器基类
    /// </summary>
    public abstract class BaseByteFrameDecoder : BaseFrameDecoder
    {
        /// <summary>
        /// 数据缓存
        /// </summary>
        protected readonly DataCache<byte> Cache = new DataCache<byte>();

        /// <summary>
        /// 数据类型
        /// </summary>
        public sealed override FrameDataType DataType => FrameDataType.Byte;

        /// <inheritdoc/>
        protected override void Dispose(bool disposing)
        {
            if (IsDisposed) return;

            if (disposing)
            {
                Cache.Dispose();
            }

            base.Dispose(disposing);
        }

        /// <summary>
        /// 生成当前解码器副本
        /// </summary>
        /// <returns></returns>
        public abstract BaseByteFrameDecoder Clone();

        /// <summary>
        /// 返回数据创建新的副本
        /// </summary>
        public bool ReturnNewCopy { get; set; } = false;

        /// <summary>
        /// 返回解码结果
        /// </summary>
        /// <param name="start">起始位置</param>
        /// <param name="length">长度</param>
        /// <returns></returns>
        public ReadOnlySpan<byte> GetResult(int start, int length)
        {
            if (!ReturnNewCopy)
            {
                return Cache.WrittenSpan.Slice(start, length);
            }
            else
            {
                byte[] result = new byte[length];
                Cache.WrittenSpan.Slice(start, length).CopyTo(result.AsSpan());
                return result;
            }
        }

        /// <summary>
        /// 解码
        /// </summary>
        /// <param name="data">输入数据</param>
        public void Decode(ReadOnlySpan<byte> data)
        {
            CheckTimeout();
            if (data == null || data.Length == 0) return;
            if (!PrepareDecode(data)) return;
            if (Cache.WrittenCount + data.Length >= CacheMaxSize) Reset();
            Cache.Add(data);
            if (Cache.WrittenCount < FrameExceptDataLength) return;
            Decoding();
            FinishDecode();
            DoAfterDecoder(this);
        }

        /// <summary>
        /// 准备数据解码
        /// </summary>
        /// <param name="data">输入数据</param>
        /// <returns>判断此数据是否可以解码</returns>
        protected virtual bool PrepareDecode(ReadOnlySpan<byte> data)
        {
            if (data == null || data.Length == 0)
            {
                DecoderError("The data to be decoded is null.", data);
                return false;
            }

            return true;
        }

        /// <summary>
        /// 缓存复位
        /// </summary>
        public override void Reset()
        {
            Cache.Clear();
        }

        /// <summary>
        /// 解码输出事件
        /// </summary>
        public event OnByteFrameArgs OnDecoder;

        /// <summary>
        /// 调用解码输出事件
        /// </summary>
        /// <param name="e"></param>
        protected void InvokeOnDecoder(ByteFrameEventArgs e) => OnDecoder?.Invoke(this, e);

        /// <summary>
        /// 解码错误事件
        /// </summary>
        public event OnByteFrameDecoderError OnDecoderError;

        /// <summary>
        /// 解码错误事件
        /// </summary>
        /// <param name="error">错误描述</param>
        /// <param name="data">数据</param>
        protected void DecoderError(string error, ReadOnlySpan<byte> data)
        {
            OnDecoderError?.Invoke(this, new ByteDecoderExceptionEventArgs(error, data));
        }
    }
}
